#pragma once

#include "../muduo/include/muduo/net/TcpServer.h"
#include "../muduo/include/muduo/net/EventLoop.h"
#include "../muduo/include/muduo/net/TcpConnection.h"
#include "../muduo/include/muduo/net/Buffer.h"
#include "../muduo/include/muduo/net/EventLoopThread.h"
#include "../muduo/include/muduo/base/CountDownLatch.h"
#include "../muduo/include/muduo/net/TcpClient.h"
#include "abstract.hpp"
#include "detail.hpp"
#include "fields.hpp"
#include "message.hpp"
#include <mutex>

namespace MuduoNet
{
    using namespace Abstract;
    using namespace std;
    using namespace Message;
    class MuduoBuffer : public BaseBuffer
    {
    public:
        using ptr = shared_ptr<MuduoBuffer>;
        MuduoBuffer(muduo::net::Buffer *buf) : _buf(buf)
        {
        }
        virtual size_t ReadAbleSize() override
        {
            return _buf->readableBytes();
        }
        // muduo库是一个网咯库，从缓冲区取一个四字节整形，进行网络字节序替换
        virtual int32_t PeekInt32() override
        {
            return _buf->peekInt32();
        }
        virtual void RetrieveInt32() override
        {
            return _buf->retrieveInt32();
        }
        virtual int32_t ReadInt32() override
        {
            return _buf->readInt32();
        }
        virtual string RetrieveAsString(size_t len) override
        {
            return _buf->retrieveAsString(len);
        }

    private:
        muduo::net::Buffer *_buf;
    };
    class MuduoBufferFactory
    {
    public:
        template <typename... Args>
        static BaseBuffer::ptr create(Args &&...args)
        {
            return make_shared<MuduoBuffer>(forward<Args>(args)...);
        }
    };

    class LVProtocol : public BaseProtocol
    {
    public:
        using ptr = shared_ptr<LVProtocol>;
        // 数据类型
        //  |--Len--|--VALUE--|
        //  |--Len--|--mtype--|--idlen--|--id--|--body--|
        // 判断缓冲区中的数量是否足够一个一条消息处理
        virtual bool CanProcessed(const BaseBuffer::ptr &buf) override
        {
            if (buf->ReadAbleSize() < _lenFieldsLength)
            {
                return false;
            }
            int32_t total_len = buf->PeekInt32();
            if (buf->ReadAbleSize() < (total_len + _lenFieldsLength)) //???
            {
                return false;
            }
            return true;
        }
        virtual bool OnMessage(const BaseBuffer::ptr &buf, BaseMessage::ptr &msg) override
        {
            // 默认是能读一条完整消息
            int32_t total_len = buf->ReadInt32();
            Mtype mtype = (Mtype)buf->ReadInt32();
            int32_t idlen = buf->ReadInt32();
            int32_t bodylen = total_len - idlen - _idlenFieldsLength - _mtypeFieldsLength;
            string id = buf->RetrieveAsString(idlen);
            string body = buf->RetrieveAsString(bodylen);
            msg = MessageFactory::create(mtype);
            if (msg.get() == nullptr)
            {
                ELog("消息类型错误，构造消息对象失败!");
                return false;
            }
            bool ret = msg->Deserialize(body);
            if (ret == false)
            {
                ELog("消息正文反序列化失败!");
                return false;
            }
            msg->SetId(id);
            msg->SetMtype(mtype);
            return true;
        }
        virtual string Serialize(const BaseMessage::ptr &msg) override
        {
            // |--Len--|--mtype--|--idlen--|--id--|--body--|
            string body = msg->Serialize();
            string id = msg->GetRid();
            // 前面加htonl为了后续规范性
            auto mtype = htonl((int32_t)msg->GetMtype());
            int32_t idlen = htonl(id.size());
            int32_t h_totallen = _mtypeFieldsLength + _idlenFieldsLength + id.size() + body.size();
            int32_t n_totallen = htonl(h_totallen);
            string res;
            res.reserve(h_totallen);
            res.append((char *)&n_totallen, _lenFieldsLength);
            res.append((char *)&mtype, _mtypeFieldsLength);
            res.append((char *)&idlen, _idlenFieldsLength);
            res.append(id);
            res.append(body);
            return res;
        }

    private:
        const size_t _lenFieldsLength = 4;
        const size_t _mtypeFieldsLength = 4;
        const size_t _idlenFieldsLength = 4;
    };
    class ProtocolFactory
    {
    public:
        template <typename... Args>
        static BaseProtocol::ptr create(Args &&...args)
        {
            return std::make_shared<LVProtocol>(std::forward<Args>(args)...);
        }
    };

    class MuduoConnection : public BaseConnection
    {
    public:
        using ptr = shared_ptr<MuduoConnection>;
        MuduoConnection(const muduo::net::TcpConnectionPtr &conn, const BaseProtocol::ptr &protocol)
            : _protocol(protocol), _conn(conn)
        {
        }
        virtual void Send(const BaseMessage ::ptr &msg) override
        {
            string body = _protocol->Serialize(msg);
            _conn->send(body);
        }
        virtual void ShutDown() override
        {
            _conn->shutdown();
        }
        virtual bool Connected() override
        {
            _conn->connected();
            return true;
        }

    private:
        BaseProtocol::ptr _protocol;
        muduo::net::TcpConnectionPtr _conn;
    };
    class ConnectionFactory
    {
    public:
        template <typename... Args>
        static BaseConnection::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoConnection>(std::forward<Args>(args)...);
        }
    };

    class MuduoServer : public BaseServer
    {
    public:
        using ptr = shared_ptr<MuduoServer>;
        MuduoServer(int port)
            : _server(&_baseloop,
                      muduo::net::InetAddress("0.0.0.0", port),
                      "MuduoServer", muduo::net::TcpServer::kReusePort),
              _protocol(ProtocolFactory::create())
        {
        }
        void Start()
        {
            // 设置连接事件
            _server.setConnectionCallback(std::bind(&MuduoServer::OnConnection, this, std::placeholders::_1));
            // 设置连接消息回调
            _server.setMessageCallback(std::bind(&MuduoServer::OnMessage, this,
                                                 std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
            _server.start();  // 开始监听
            _baseloop.loop(); // 开始死循环事件监控
        }

    private:
        void OnConnection(const muduo::net::TcpConnectionPtr &conn)
        {
            if (conn->connected())
            {
                std::cout << "连接成功！" << std::endl;
                auto muduo_conn = ConnectionFactory::create(conn, _protocol);
                {
                    unique_lock<mutex> look(_mutex);
                    _conns.insert(make_pair(conn, muduo_conn));
                }
                if (_cb_connection)
                    _cb_connection(muduo_conn);
            }
            else
            {
                std::cout << "连接断开！" << std::endl;
                BaseConnection::ptr muduo_conn;
                {
                    unique_lock<mutex> look(_mutex);
                    auto it = _conns.find(conn);
                    if (it == _conns.end())
                    {
                        muduo_conn->ShutDown();
                        return;
                    }
                    muduo_conn = it->second;
                    _conns.erase(conn);
                }
                if (_cb_close)
                    _cb_close(muduo_conn);
            }
        }
        void OnMessage(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buffer, muduo::Timestamp)
        {
            DLog("连接有数据到来！");
            auto base_buf = MuduoBufferFactory::create(buffer);
            while (1)
            {
                if (_protocol->CanProcessed(base_buf) == false)
                {
                    if (base_buf->ReadAbleSize() > MaxDataSize)
                    {
                        ELog("数据过大！");
                        conn->shutdown();
                        return;
                    }
                    break;
                }
                BaseMessage::ptr msg;
                bool ret = _protocol->OnMessage(base_buf, msg);
                if (ret == false)
                {
                    ELog("缓冲区数据错误");
                    conn->shutdown();
                    return;
                }
                DLog("消息反序列化成功！");
                BaseConnection::ptr base_conn;
                {
                    unique_lock<mutex> look(_mutex);
                    auto it = _conns.find(conn);
                    if (it == _conns.end())
                    {
                        return;
                    }
                    base_conn = it->second;
                }
                DLog("调用回调函数处理");
                if (_cb_message)
                    _cb_message(base_conn, msg);
            }
        }

    private:
        const size_t MaxDataSize = (1 << 16);
        BaseProtocol ::ptr _protocol;
        muduo::net::EventLoop _baseloop;
        muduo::net::TcpServer _server;
        mutex _mutex;
        unordered_map<muduo::net::TcpConnectionPtr, BaseConnection::ptr> _conns;
    };
    class ServerFactory
    {
    public:
        template <typename... Args>
        static BaseServer::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoServer>(std::forward<Args>(args)...);
        }
    };

    class MuduoClient : public BaseClient
    {
    public:
        using ptr = shared_ptr<MuduoClient>;
        MuduoClient(const std::string &sip, int sport)
            : _protocol(ProtocolFactory::create()),
              _loop(_loopthread.startLoop()),
              _downlatch(1), // 只需要一次连接，连接完就直接唤醒
              _client(_loop, muduo::net::InetAddress(sip, sport), "MuduoClient")
        {
        }
        virtual void Connect() override
        {
            DLog("设置回调函数，连接服务器！");
            // 设置连接事件
            _client.setConnectionCallback(std::bind(&MuduoClient::OnConnection, this, std::placeholders::_1));
            // 设置连接消息回调
            _client.setMessageCallback(std::bind(&MuduoClient::OnMessage, this,
                                                 std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            // 连接服务器
            _client.connect();
            _downlatch.wait(); // 等待连接后唤醒
            DLog("连接服务器成功！");
        }

        virtual void Shutdown() override
        {
            return _client.disconnect();
        }
        virtual bool Send(const BaseMessage::ptr &msg) override
        {
            if(_conn->Connected()==false)
            {
                ELog("连接已断开");
                return false;
            }
            _conn->Send(msg);
            return true;
        }
        virtual BaseConnection::ptr Conncection() override
        {
            return _conn;
        }
        virtual bool Connected() override
        {
            return (_conn&&_conn->Connected());
        }

    private:
        void OnConnection(const muduo::net::TcpConnectionPtr &conn)
        {
            if (conn->connected())
            {
                std::cout << "连接建立！" << std::endl;
                _conn =ConnectionFactory::create(conn,_protocol);
                _downlatch.countDown(); // 连接数--一定是上边赋值完成后再唤醒上边
            }
            else
            {
                std::cout << "连接断开！" << std::endl;
                _conn.reset();
            }
        }
        void OnMessage(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buffer, muduo::Timestamp)
        {
            DLog("连接有数据到来！");
            auto base_buf = MuduoBufferFactory::create(buffer);
            while (1)
            {
                if (_protocol->CanProcessed(base_buf) == false)
                {
                    if (base_buf->ReadAbleSize() > MaxDataSize)
                    {
                        ELog("缓冲区数据过大！");
                        conn->shutdown();
                        return;
                    }
                    break;
                }
                DLog("消息反序列化成功！");
                BaseMessage::ptr msg;
                bool ret = _protocol->OnMessage(base_buf, msg);
                if (ret == false)
                {
                    ELog("缓冲区数据错误");
                    conn->shutdown();
                    return;
                }
                DLog("消息解析成功，调用回调函数处理");
                if (_cb_message)
                    _cb_message(_conn, msg);
            }
        }
         private:
            const size_t MaxDataSize = (1 << 16);
            BaseProtocol::ptr _protocol;
            BaseConnection::ptr _conn;
            muduo::CountDownLatch _downlatch;
            muduo::net::EventLoopThread _loopthread;
            muduo::net::EventLoop *_loop;
            muduo::net::TcpClient _client;

    };
    class ClientFactory
    {
        public:
        template <typename... Args>
        static BaseClient::ptr create(Args &&...args)
        {
            return std::make_shared<MuduoClient>(std::forward<Args>(args)...);
        }
    };
}
